using System.Threading;
using System.Threading.Tasks;

namespace nice_157_advices.Advices
{
    /// <summary>
    /// 83.小心Parallel中的陷阱
    /// </summary>
    public static class _83
    {
        /**
         * Parallel.For<T>(int 开始索引, int 结束索引, func<T> init, func<int, ParallelLoopState, T, T> body, action<T> finally)
         * init 参数：如果 Parallel 为我们新起了一个线程，那么久会执行初始化的任务。
         * finally 参数：每个线程结束的时候，执行收尾工作。
         */

        public static void Go ()
        {
            int[] nums = new int[] { 1, 2, 3, 4 };
            int total = 0;
            Parallel.For<int> (0, nums.Length, () =>
                {
                    return 1;
                },
                (i, loopState, subtotal) =>
                {
                    // subtotal = 1
                    subtotal += nums[i];
                    return subtotal;
                }, (x) =>
                {
                    // ===> total = total + x
                    Interlocked.Add (ref total, x);
                    System.Console.WriteLine("结束一个线程");
                }
            );

            /**
             * For方法采用并发的方式来启动循环体中的每个任务，这意味着，任务是交给线程池去管理的。在上面的代码中，循环次数共计4次，实际运行时调度启动的后台线程也就只有一个或两个。这就是并发的优势，也是线程池的优势，Parallel通过内部的调度算法，最大化地节约了线程的消耗。
             */

            // 循环四次 理论结果应该是14 但有可能出现 11 12 13 情况
            System.Console.WriteLine ($"total={total}");
            // System.Console.ReadKey();
        }
    }
}